package com.cyy.operation.appium;

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MultiTouchAction;
import io.appium.java_client.TouchAction;
import io.appium.java_client.touch.WaitOptions;
import io.appium.java_client.touch.offset.PointOption;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.*;
import org.openqa.selenium.logging.Logs;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
public abstract class AbstractAppium {

    private long timeout_locate_element;// 查找元素的最大时长
    private Dimension dimension;

    /**
     * 获取日志信息
     * @param driver
     * @return
     */
    public Logs log(AppiumDriver driver) {
        Logs logs = driver.manage().logs();
        return logs;
    }


    /**
     * 退出app
     */
    public void quit(AppiumDriver driver) {
        driver.quit();
    }

    /**
     * 设置动态等待时间
     *
     * @param timeout 最大等待时间,单位毫秒
     */
    public void set_implicitly_wait(AppiumDriver driver,long timeout) {
        driver.manage().timeouts().implicitlyWait(timeout, TimeUnit.NANOSECONDS);
    }

    /**
     * 强制等待
     *
     * @param time 等待时间 单位毫秒
     */
    public void sleep(long time) throws InterruptedException {
        Thread.sleep(time);
    }

    /**
     * 显示等待，即等待指定元素在某个状态之前一直等待
     *
     * @param condition 元素状态
     * @param timeout   等待时长
     */
    public Object explicitWait(AppiumDriver driver,ExpectedCondition condition, long timeout) {
        WebDriverWait wait = new WebDriverWait(driver, timeout);
        return wait.until(condition);
    }

    /**
     * 流畅等待，在指定元素在指定的状态下显示时，退出等待，直到等待最大时间
     *
     * @param condition   元素状态
     * @param timeout     等待最大时长
     * @param pollingTime 滚动检查的时间
     */
    public Object fluentWait(AppiumDriver driver,ExpectedCondition condition, long timeout, long pollingTime) {
        FluentWait wait = new FluentWait<WebDriver>(driver);
        Object o = null;
        try {
            o = wait.withTimeout(Duration.ofMillis(timeout))
                    .pollingEvery(Duration.ofMillis(pollingTime))
                    .ignoring(NoSuchElementException.class)
                    .ignoring(TimeoutException.class)
                    .until(condition);

        } catch (Exception e) {
            o = null;
//            e.printStackTrace();
            String name = e.getClass().getName();
            if (!(name.contains("NoSuchElementException") || name.contains("TimeoutException"))) {
                throw e;
            }
        }
        return o;
    }

    /**
     * 按下指定的按键
     *
     * @param keys
     */
    public void sendKeys(AppiumDriver driver,By by, String keys) {
        WebElement element = (WebElement) fluentWait(driver,(ExpectedCondition) ExpectedConditions.elementToBeClickable(by), timeout_locate_element, 200l);
        element.sendKeys(keys);
    }

    public void set_timeout_of_locate(long timeout) {
        this.timeout_locate_element = timeout;
    }


    /**
     * 根据bundleId来判断该应用是否已经安装
     *
     * @param bundleId
     * @return
     */
    public boolean appInstalled(AppiumDriver driver,String bundleId) {
        return driver.isAppInstalled(bundleId);
    }

    /**
     * 安装app，appPath为应用的本地路径
     *
     * @param appPath app的path
     */
    public void installApp(AppiumDriver driver,String appPath) {
        driver.installApp(appPath);
    }

    /**
     * 卸载app.bundleId在android中代表的是package，而在ios中有专门的bundleId号
     *
     * @param bundleId
     */
    public void uninstallApp(AppiumDriver driver,String bundleId) {
        driver.removeApp(bundleId);
    }

    /**
     * 关闭应用，其实就是按home键把应用置于后台
     */
    public void closeApp(AppiumDriver driver) {
        driver.closeApp();
    }

    /**
     * 启动应用
     */
    public void launchApp(AppiumDriver driver) {
        driver.launchApp();
    }

    /**
     * 先closeApp然后在launchAPP
     */
    public void restartApp(AppiumDriver driver) {
        driver.resetApp();
    }


    /**
     * 将设备上的文件pull到本地硬盘上
     *
     * @param remotePath
     */
    public byte[] pullFile(AppiumDriver driver,String remotePath) {
        return driver.pullFile(remotePath);
    }

    /**
     * 将设备上的文件夹pull到本地硬盘上，一般远程文件为/data/local/tmp下的文件。
     *
     * @param remotePath
     */
    public byte[] pullFolder(AppiumDriver driver,String remotePath) {
        return driver.pullFolder(remotePath);
    }



    /**
     * 与resetApp类似，区别是resetApp关闭后立即启动，而这个方法是关闭后等待seconds秒后再启动。
     *
     * @param seconds
     */
    public void runAppInBackground(AppiumDriver driver,int seconds) {
        driver.runAppInBackground(Duration.ofSeconds(seconds));
    }


    /**
     * 执行一个touch动作，该touch动作是由TouchAction封装的。
     *
     * @param touchAction
     */
    public void performTouchAction(AppiumDriver driver,io.appium.java_client.TouchAction touchAction) {
        driver.performTouchAction(touchAction);
    }

    /**
     * 执行多步touch动作，由MultiTouchAction封装的多步操作。
     *
     * @param multiAction
     */
    public void performMultiTouchAction(AppiumDriver driver,MultiTouchAction multiAction) {
        driver.performMultiTouchAction(multiAction);
    }


    /**
     * 从(startx,starty)滑到（endx,endy），分duration步滑，
     *
     * @param startx   起始点x坐标
     * @param starty   起始点y坐标
     * @param endx     结束点x
     * @param endy     结束点y
     * @param duration 滑动需要的时间长度,单位毫秒
     */
    public void swipe(AppiumDriver driver,int startx, int starty, int endx, int endy, int duration) {
        PointOption start = PointOption.point(startx, starty);
        PointOption end = PointOption.point(endx, endy);
        this.swipe(driver,start,end,duration);
    }

    public void swipe(AppiumDriver driver,PointOption start, PointOption end, int duration) {
        TouchAction action = new TouchAction(driver);
        WaitOptions waitOption = WaitOptions.waitOptions(Duration.ofMillis(duration));
        action.press(start).waitAction(waitOption).moveTo(end).release().perform();
    }

    /**
     * 在屏幕上多点滑动
     * @param driver
     * @param points 要滑动的所有点的集合
     * @param duration 点之间滑动花费时间
     */
    public void swipe(AppiumDriver driver, LinkedList<PointOption> points, int duration) {
        TouchAction action = new TouchAction(driver);
        WaitOptions waitOption = WaitOptions.waitOptions(Duration.ofMillis(duration));
        String path = "";
        for (int i = 0; i < points.size(); i++) {
            if (i==0) {
                path += points.get(i).build();
                action = action.press(points.get(i)).waitAction(waitOption);
            }else {
                path += "--> " + points.get(i).build();
                action = action.moveTo(points.get(i)).waitAction(waitOption);
            }
        }

        action.release().perform();
        log.info("绘制路线:" + path);
    }

    /**
     * 向左滑动屏幕，时长为duratio毫秒
     *
     * @param duration 滑动屏幕花费时长
     */
    public void swipe_left(AppiumDriver driver,int duration) {
        Dimension dimension = screen_size(driver);
        int height = dimension.getHeight();
        int width = dimension.getWidth();
        this.swipe(driver,width * 2 / 3, height / 4, width / 3, height / 4, duration);
    }

    /**
     * 向右滑动屏幕，时长为duratio毫秒
     *
     * @param duration 滑动屏幕花费时长
     */
    public void swipe_right(AppiumDriver driver,int duration) {
        Dimension dimension = screen_size(driver);
        int height = dimension.getHeight();
        int width = dimension.getWidth();
        this.swipe(driver,width / 3, height / 4, width * 2 / 3, height / 4, duration);
    }

    /**
     * 向上滑动屏幕，时长为duratio毫秒
     *
     * @param duration 滑动屏幕花费时长
     */
    public void swipe_top(AppiumDriver driver,int duration) {
        Dimension dimension = screen_size(driver);
        int height = dimension.getHeight();
        int width = dimension.getWidth();
        this.swipe(driver,1 / 2 * width, height * 3 / 4, width / 2, height / 4, duration);
    }

    /**
     * 向下滑动屏幕，时长为duratio毫秒
     *
     * @param duration 滑动屏幕花费时长
     */
    public void swipe_down(AppiumDriver driver,int duration) {
        Dimension dimension = screen_size(driver);
        int height = dimension.getHeight();
        int width = dimension.getWidth();
        this.swipe(driver,1 / 2 * width, height / 4, width / 2, height * 3 / 4, duration);
    }

    /**
     * 获取屏幕的尺寸
     */
    public Dimension screen_size(AppiumDriver driver) {
        dimension = driver.manage().window().getSize();
        return dimension;
    }

    /**
     * 程序是否安装，已安装返回true
     *
     * @param series
     * @return
     */
    public boolean is_app_installed(AppiumDriver driver,String series) {
        return driver.isAppInstalled(series);
    }



    /**
     * 设置屏幕横屏或者竖屏
     *
     * @param orientation
     */
    public void rotate(AppiumDriver driver,ScreenOrientation orientation) {
        driver.rotate(orientation);
    }

    /**
     * 获取当前屏幕的方向
     */
    public ScreenOrientation getOrientation(AppiumDriver driver) {
        return driver.getOrientation();
    }

    /**
     * 利用accessibility id来获取单个控件
     *
     * @param using
     * @param timeout 查询控件超时时间
     */
    public WebElement findElementByAccessibilityId(AppiumDriver driver,String using, long timeout) {
        WebElement o = (WebElement) this.fluentWait(driver,ExpectedConditions.visibilityOf(driver.findElementByAccessibilityId(using)), timeout, 200);
        return o;

    }


    /**
     * 在控件上执行press操作。
     *
     * @param el
     */
    public void press(WebElement el) {
        el.click();
    }

    /**
     * 以el为目标，从另一个点移动到该目标上
     *
     * @param el   要移动的元素
     * @param x    目标点的x坐标
     * @param y    目标点的y坐标
     * @param time 移动元素到指定目标点花费的时长,单位毫秒
     */
    public void moveTo(AppiumDriver driver,WebElement el, int x, int y, long time) {
        TouchAction action = new TouchAction<>(driver);
        Point elLocation = el.getLocation();
        PointOption elpoint = PointOption.point(elLocation.x, elLocation.y);
        PointOption goalpoint = PointOption.point(x, y);
        WaitOptions wait = WaitOptions.waitOptions(Duration.ofMillis(time));
        action.press(elpoint).waitAction(wait).moveTo(goalpoint).release().perform();
    }

    /**
     * 把一个元素from移动到另一个元素to上
     *
     * @param from 移动元素1
     * @param to   元素2
     * @param time 移动元素花费的时长
     */
    public void moveTo(AppiumDriver driver,WebElement from, WebElement to, long time) {
        TouchAction action = new TouchAction<>(driver);
        Point elfromLocation = from.getLocation();
        PointOption elfrompoint = PointOption.point(elfromLocation.x, elfromLocation.y);
        Point eltoLocation = to.getLocation();
        PointOption eltopoint = PointOption.point(eltoLocation.x, eltoLocation.y);
        WaitOptions wait = WaitOptions.waitOptions(Duration.ofMillis(time));
        action.press(elfrompoint).waitAction(wait).moveTo(eltopoint).release().perform();
    }

    /**
     * 点击设备屏幕中心点
     * @param driver
     * @param time 点击时长
     */
    public void click_center(AppiumDriver driver,long time) {
        Dimension dimension = screen_size(driver);
        int height = dimension.getHeight();
        int width = dimension.getWidth();
        WaitOptions wait = WaitOptions.waitOptions(Duration.ofMillis(time));
        TouchAction action = new TouchAction<>(driver);
        action.press(PointOption.point(width/2,height/2)).waitAction(wait).release().perform();
    }

    /**
     * 控件长按
     *
     * @param el
     */
    public void longPress(AppiumDriver driver,WebElement el) {
        PointOption point = PointOption.point(el.getLocation().x, el.getLocation().y);
        new TouchAction<>(driver).longPress(point).perform();
    }

}
